package com.crane.oss.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.crane.oss.school.bean.resp.StudentResp;
import com.crane.oss.school.bean.resp.StudentUserResp;
import com.crane.oss.school.dao.ClassesInfoDao;
import com.crane.oss.school.dao.StudentClassDao;
import com.crane.oss.school.dao.StudentDao;
import com.crane.oss.school.model.ClassesInfo;
import com.crane.oss.school.model.Student;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import java.util.Set;
import javax.annotation.Resource;

import org.apache.http.Consts;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crane.common.core.exception.AppException;
import com.crane.common.core.exception.BasicErrorCode;
import com.crane.common.core.exception.BizException;
import com.crane.common.core.exception.SysException;
import com.crane.common.core.utils.BaseContextHandler;
import com.crane.common.core.utils.CollectionUtil;
import com.crane.common.core.utils.StringUtil;
import com.crane.common.framework.constant.AppSessionConstant;
import com.crane.common.framework.jwt.JwtUserToken;
import com.crane.common.framework.jwt.JwtUtil;
import com.crane.common.framework.utils.BeanUtil;
import com.crane.oss.app.bean.req.AppBindingStudentReq;
import com.crane.oss.app.bean.req.AppUnBindingStudentReq;
import com.crane.oss.app.bean.req.AppUpdatePicReq;
import com.crane.oss.app.bean.request.QueryAppUserReq;
import com.crane.oss.app.bean.request.appuser.AddAppUserReq;
import com.crane.oss.app.bean.request.appuser.AppLoginCodeRequest;
import com.crane.oss.app.bean.request.appuser.AppLoginRequest;
import com.crane.oss.app.bean.request.appuser.ForgetPasswordRequest;
import com.crane.oss.app.bean.request.appuser.UpdateAppUserReq;
import com.crane.oss.app.bean.request.appuser.WxLoginParam;
import com.crane.oss.app.bean.resp.AppClassesInfoResp;
import com.crane.oss.app.bean.resp.AppStudentInfo;
import com.crane.oss.app.bean.resp.AppUserInfoResp;
import com.crane.oss.app.bean.resp.AppUserResp;
import com.crane.oss.app.bean.resp.AppUserStudentInfoResp;
import com.crane.oss.app.constant.WXLoginConstant;
import com.crane.oss.app.dao.AppUserDao;
import com.crane.oss.app.model.AppUser;
import com.crane.oss.app.service.AppUserService;
import com.crane.oss.common.bean.request.AddDynamicCodeRequest;
import com.crane.oss.common.bean.request.QueryDynamicCodeRequest;
import com.crane.oss.common.bean.request.SendSMSRequest;
import com.crane.oss.common.bean.resp.SendSMSResp;
import com.crane.oss.common.service.AliyunSMSService;
import com.crane.oss.common.service.DynamicCodeService;
import com.crane.oss.common.service.SmsSendRecordService;
import com.crane.oss.enums.DynamicCodeTypeEnum;
import com.crane.oss.properties.SmsConfigProperties;
import com.crane.oss.school.bean.request.studentuser.AddStudentUserReq;
import com.crane.oss.school.bean.resp.ClassesInfoResp;
import com.crane.oss.school.service.ClassesInfoService;
import com.crane.oss.school.service.StudentService;
import com.crane.oss.school.service.StudentUserService;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zhenxing.pi
 * @email ccsu123456@qq.com
 * @date 2022年03月20日 23时05分57秒
 */
@Service
@Slf4j
public class AppUserServiceImpl implements AppUserService {

	@Resource
	protected AppUserDao appUserDao;

	@Resource
	protected StudentDao studentDao;

	@Autowired
	RedisTemplate redisTemplate;

	@Autowired
	DynamicCodeService dynamicCodeService;

	@Autowired
	SmsConfigProperties smsConfigProperties;

	@Autowired
	private AliyunSMSService aliyunSMSService;

	@Autowired
	private SmsSendRecordService smsSendRecordService;

	@Autowired
	@Lazy
	private StudentService studentService;

	@Autowired
	private ClassesInfoService classesInfoService;

	@Autowired
	private StudentUserService studentUserService;

	@Resource
	protected ClassesInfoDao classesInfoDao;

	@Override
	public List<AppUserResp> queryAppUserForStudent(Long studentId) {
		log.info("AppUserServiceImpl queryAppUserForStudent studentId={}", studentId);
		List<AppUser> queryList = appUserDao.queryAppUser(studentId);
		List<AppUserResp> returnList = BeanUtil.copyList(queryList, AppUserResp.class);
		return returnList;
	}

	@Override
	public AppUserResp queryAppUser(String phone) {
		log.info("AppUserServiceImpl queryAppUser phone={}", phone);
		AppUser appUser = appUserDao.selectOne(
				Wrappers.lambdaQuery(AppUser.class).eq(AppUser::getMobile, phone).in(AppUser::getStatus, 2, 1));
		if (null == appUser) {
			return null;
		}
		AppUserResp appUserResp = new AppUserResp();
		BeanUtil.copyProperties(appUser, appUserResp);
		return appUserResp;
	}

	@Override
	public AppUserResp queryAppUser(Long id) {
		log.info("AppUserServiceImpl queryAppUser id={}", id);
		AppUser appUser = appUserDao.selectById(id);
		if (null == appUser) {
			throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "用户");
		}
		AppUserResp appUserResp = new AppUserResp();
		BeanUtil.copyProperties(appUser, appUserResp);
		return appUserResp;
	}

	@Override
	public IPage<AppUserResp> queryList(QueryAppUserReq queryRequest) {
		log.info("AppUserBOImpl queryList queryRequest={}", queryRequest);
		IPage<AppUserResp> result = new Page<>();
		result.setCurrent(queryRequest.getPageNo());
		result.setPages(queryRequest.getPageSize());

		LambdaQueryWrapper<AppUser> queryAppUserWrapper = new LambdaQueryWrapper<>();
		IPage<AppUser> page = appUserDao.selectPage(new Page<>(queryRequest.getPageNo(), queryRequest.getPageSize()),
				queryAppUserWrapper);
		return page.convert(u -> {
			AppUserResp v = new AppUserResp();
			BeanUtils.copyProperties(u, v);
			return v;
		});
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Long save(AddAppUserReq addRequest) {
		log.info("AppUserServiceImpl save request={}", addRequest);
		AppUser appUser = new AppUser();
		BeanUtil.copyProperties(addRequest, appUser);
		try {
			appUserDao.insert(appUser);
		} catch (Exception e) {
			log.info("AppUserServiceImpl save e={}", e);
			throw new SysException();
		}
		return appUser.getAppUserId();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int update(UpdateAppUserReq updateRequest) {
		log.info("AppUserServiceImpl update request={}", updateRequest);
		AppUser appUser = new AppUser();
		BeanUtil.copyProperties(updateRequest, appUser);
		int i = 0;
		try {
			i = appUserDao.updateById(appUser);
		} catch (Exception e) {
			log.info("AppUserServiceImpl add e={}", e);
			throw new SysException();
		}
		return i;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int delete(Long id) {
		log.info("AppUserServiceImpl delete id={}", id);
		return appUserDao.deleteById(id);
	}

	@Override
	public AppUserResp queryUserByMobile(String mobile) {
		AppUser appUser = appUserDao.selectOne(
				Wrappers.lambdaQuery(AppUser.class).eq(AppUser::getMobile, mobile).in(AppUser::getStatus, 2, 1));
		if (null == appUser) {
			return null;
		}
		AppUserResp appUserResp = new AppUserResp();
		BeanUtil.copyProperties(appUser, appUserResp);
		return appUserResp;
	}
	@Override
	public AppUserInfoResp weChatLogin(WxLoginParam request) {
		JSONObject wxResponse = getWxResponse(request.getCode());
		JSONObject wxResponseToken = getWxResponseToken();
		String openid = wxResponse.get("openid").toString();
		String unionId = wxResponse.getString("unionId");
		String accessToken= wxResponseToken.getString("access_token");
		JSONObject wxResponsePhone = getWxResponsePhone(request.getPhoneCode(), accessToken);
		String phone = wxResponsePhone.getJSONObject("phone_info").getString("phoneNumber");
		if (phone == null) {
			throw new BizException("手机号码不能为空");
		}
		AppUser appUser = appUserDao.selectOne(
				Wrappers.lambdaQuery(AppUser.class).eq(AppUser::getMobile, phone));
		if (null == appUser) {
			appUser = new AppUser();
			appUser.setUserType(2);
			appUser.setMobile(phone);
			appUser.setUnionId(unionId);
			appUser.setOpenId(openid);
			appUser.setStatus(2);
			appUser.setCreateTime(new Date());
			appUserDao.insert(appUser);
		} else {
			if (appUser.getUserType().equals(1)) {
				appUser.setUserType(3);
			}
			appUser.setOpenId(openid);
			appUser.setUnionId(unionId);
			appUserDao.updateById(appUser);

		}
		JwtUserToken user = new JwtUserToken(appUser.getAppUserId().toString());
		String token = JwtUtil.generateHSToken(user);
		AppUserInfoResp appUserInfoResp = new AppUserInfoResp();
		BeanUtil.copyProperties(appUser, appUserInfoResp);
		appUserInfoResp.setToken(token);
		redisTemplate.opsForValue().set(AppSessionConstant.REDIS_SESSION_PREFIX + appUser.getAppUserId(), token);
		return appUserInfoResp;
	}

	@Override
	public String loginCode(AppLoginCodeRequest request) {
		String phone = request.getPhone();
		// 创建并发送登录验证码
		AddDynamicCodeRequest addDynamicCodeRequest = new AddDynamicCodeRequest();
		addDynamicCodeRequest.setPhone(phone);
		if (StringUtil.isEmpty(request.getBizType())) {
			addDynamicCodeRequest.setBizType(DynamicCodeTypeEnum.LOGIN_CODE.getValue());
			addDynamicCodeRequest.setMemo(DynamicCodeTypeEnum.LOGIN_CODE.getRemark());
		} else {
			DynamicCodeTypeEnum dynamicCodeTypeEnum = DynamicCodeTypeEnum
					.getDynamicCodeTypeEnumByValue(request.getBizType());
			if (null == dynamicCodeTypeEnum) {
				throw new AppException("验证码类型不存在");
			}
			addDynamicCodeRequest.setBizType(dynamicCodeTypeEnum.getValue());
			addDynamicCodeRequest.setMemo(dynamicCodeTypeEnum.getRemark());
		}
		String dynamicCode = dynamicCodeService.getCode(addDynamicCodeRequest, null);

		SendSMSRequest sendRequest = new SendSMSRequest();
		sendRequest.setMsgTypeNo(smsConfigProperties.getVerifyCodeTemplate());
		sendRequest.setCode(dynamicCode);
		sendRequest.setPhoneNumber(phone);

		SendSMSResp sendResp = aliyunSMSService.sendSms(sendRequest);
		smsSendRecordService.save(sendResp);
		return phone;
	}

	@Override
	public AppUserInfoResp login(AppLoginRequest request) {
		AppUserInfoResp appUserInfoResp = new AppUserInfoResp();
		// 校验用户是否存在
		AppUser appUser = appUserDao.selectOne(
				Wrappers.lambdaQuery(AppUser.class).eq(AppUser::getMobile, request.getPhone()).in(AppUser::getStatus, 2, 1));

		// 验证登录验证码
		if (smsConfigProperties.isSmsOpenFlag() && StringUtil.isNotEmpty(request.getCaptcha())) {
			QueryDynamicCodeRequest queryDynamiccodeRequest = new QueryDynamicCodeRequest();
			queryDynamiccodeRequest.setBizType(DynamicCodeTypeEnum.LOGIN_CODE.getValue());
			queryDynamiccodeRequest.setDynaCode(request.getCaptcha());
			queryDynamiccodeRequest.setRefNo(request.getPhone());
			// 不抛异常则验证通过
			dynamicCodeService.validateCode(queryDynamiccodeRequest);
		}
		if (null == appUser) {
			appUser =new AppUser();
			appUser.setUserType(2);
			appUser.setMobile(request.getPhone());
			appUser.setStatus(2);
			appUser.setCreateTime(new Date());
			appUserDao.insert(appUser);
		} else {
			if (appUser.getUserType().equals(1)) {
				appUser.setUserType(3);
				appUserDao.updateById(appUser);
			}

		}
		JwtUserToken user = new JwtUserToken(appUser.getAppUserId().toString());
		String token = JwtUtil.generateHSToken(user);
		appUserInfoResp.setToken(token);
		appUserInfoResp.setId(appUser.getAppUserId());
		redisTemplate.opsForValue().set(AppSessionConstant.REDIS_SESSION_PREFIX + appUser.getAppUserId(), token);
		return appUserInfoResp;
	}

	@Override
	public void forgetPassword(ForgetPasswordRequest request) {
		// 验证 验证码
		if (smsConfigProperties.isSmsOpenFlag() && StringUtil.isNotEmpty(request.getCaptcha())) {
			QueryDynamicCodeRequest queryDynamiccodeRequest = new QueryDynamicCodeRequest();
			queryDynamiccodeRequest.setBizType(DynamicCodeTypeEnum.FORGET_PASSWORD.getValue());
			queryDynamiccodeRequest.setDynaCode(request.getCaptcha());
			queryDynamiccodeRequest.setRefNo(request.getPhone());
			// 不抛异常则验证通过
			dynamicCodeService.validateCode(queryDynamiccodeRequest);
		}

	}

	@Override
	public void loginOut() {
		redisTemplate.delete(AppSessionConstant.REDIS_SESSION_PREFIX + BaseContextHandler.getUserId());
	}

	@Override
	public void updateUserType(Integer userType) {
		Long userId = BaseContextHandler.getUserId();
		AppUser appUser = new AppUser();
		appUser.setAppUserId(userId);
		appUser.setBeforeType(userType);
		appUserDao.updateById(appUser);
	}

	@Override
	public AppUserStudentInfoResp queryStudentInfo(Long userId) {
		AppUser appUser = appUserDao.selectById(userId);
		if (null == appUser) {
			throw new AppException(BasicErrorCode.ENTITY_NOT_EXISTS, "用户");
		}
		AppUserStudentInfoResp studentInfoResp = new AppUserStudentInfoResp();
		BeanUtil.copyProperties(appUser, studentInfoResp);
		// 获取用户绑定学生的信息
		List<AppStudentInfo> studentInfos = studentService.appQueryStudentInfo(appUser.getAppUserId());
		studentInfoResp.setList(studentInfos);
		return studentInfoResp;
	}

	@Override
	public void updateUserPicUrl(AppUpdatePicReq appUpdatePicReq) {
		// 如果是老师
		if (appUpdatePicReq.getType().equals("1")) {
			AppUser appUser = new AppUser();
			appUser.setAppUserId(appUpdatePicReq.getId());
			appUser.setHeadUrl(appUpdatePicReq.getPicUrl());
			appUserDao.updateById(appUser);
			return;
		}
		// 家长修改学生
		studentService.updateUserPicUrl(appUpdatePicReq);
	}

	@Override
	public AppClassesInfoResp getClassInfo(Long classId) {
		ClassesInfoResp classesInfoResp = null;
		try {
			classesInfoResp = classesInfoService.queryClassesInfo(classId);
		} catch (Exception e) {
			log.error("班级详情查询错误", e);
		}
		if (classesInfoResp == null) {
			throw new BizException("无效二维码");
		}
		AppClassesInfoResp appClassesInfoResp = new AppClassesInfoResp();
		BeanUtil.copyProperties(classesInfoResp, appClassesInfoResp);
		return appClassesInfoResp;
	}

	@Override
	public void bindingStudent(AppBindingStudentReq appBindingStudentReq) {
		List<AppStudentInfo> list = classesInfoService.bindingStudent(appBindingStudentReq);
		if (CollectionUtil.isEmpty(list)) {
			throw new BizException("该学生不存在");
		}
		Long studentId = list.get(0).getStudentId();
		Integer integer = studentUserService.countStudentId(studentId, appBindingStudentReq.getId());
		if (integer > 0) {
			throw new BizException("您已经绑定过该学生");
		}
		integer = studentUserService.countStudentId(studentId, null);
		if (integer >= 3) {
			throw new BizException("当前学生已被3个家长绑定，不可再次绑定");
		}
		AddStudentUserReq addStudentUserReq = new AddStudentUserReq();
		addStudentUserReq.setStudentId(studentId);
		addStudentUserReq.setCreateTime(new Date());
		addStudentUserReq.setAppUserId(appBindingStudentReq.getId());
		addStudentUserReq.setCreateUserId(appBindingStudentReq.getId());
		addStudentUserReq.setIdentity(appBindingStudentReq.getIdentity());
		studentUserService.save(addStudentUserReq);
	}

	@Override
	public void unboundStudent(AppUnBindingStudentReq appBindingStudentReq) {
		studentUserService.deleteStudent(appBindingStudentReq);
	}

	@Override
	public StudentResp getDefaultStudent(Long appUserId) {
		AppUser appUser = appUserDao.selectById(appUserId);
		if (null == appUser) {
			return null;
		}
		Long studentId= null ;
        if(null != appUser.getBeforeStudent()){
			studentId= appUser.getBeforeStudent();
		}else{
			List<StudentUserResp> studentUserRespList=studentUserService.queryStudentInfo(appUserId);
			if(CollectionUtil.isNotEmpty(studentUserRespList)){
				studentId= studentUserRespList.get(0).getStudentId();
			}
		}
        if(null != studentId){
			Student student=studentDao.selectById(studentId);
			if(null == student){
				return null;
			}
			StudentResp studentResp=new StudentResp();
			BeanUtil.copyProperties(student,studentResp);
			return studentResp;
		}
		return null;
	}

	@Override
	public void updateStudentId(Long studentId) {
		Long userId = BaseContextHandler.getUserId();
		AppUser appUser = new AppUser();
		appUser.setAppUserId(userId);
		appUser.setBeforeStudent(studentId);
		appUserDao.updateById(appUser);
	}

	/**
	 * 获取请求微信完毕参数
	 *
	 * @return
	 */
	private JSONObject getWxResponse(String code) {
		RestTemplate restTemplate = new RestTemplate();// 发送request请求
		// 配置请求参数
		String params = "appid=" + WXLoginConstant.WX_SPAPPID + "&secret=" + WXLoginConstant.WX_SPSECRET + "&js_code="
				+ code + "&grant_type=" + WXLoginConstant.WX_LOGIN_GRANT_TYPE;// 参数
		String url = WXLoginConstant.WX_URL + "?" + params.trim();// 微信接口 用于查询oponid
		String response = restTemplate.getForObject(url, String.class);
		return JSONObject.parseObject(response);
	}
	/**
	 * 获取请求微信完毕参数
	 *
	 * @return
	 */
	private JSONObject getWxResponseToken() {
		RestTemplate restTemplate = new RestTemplate();// 发送request请求
		// 配置请求参数
		String params = "appid=" + WXLoginConstant.WX_SPAPPID + "&secret=" + WXLoginConstant.WX_SPSECRET +"&grant_type=" + "client_credential";// 参数
		String url = "https://api.weixin.qq.com/cgi-bin/token" + "?" + params.trim();// 微信接口 用于查询oponid
		String response = restTemplate.getForObject(url, String.class);
		return JSONObject.parseObject(response);
	}

	private JSONObject getWxResponsePhone(String code,String token) {
		RestTemplate restTemplate = new RestTemplate();// 发送request请求

		String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+token;
		Map<String, String> map =new HashMap();
		map.put("code",code);
		HttpHeaders headers = new HttpHeaders();
		MediaType type = MediaType.parseMediaType("text/html; charset=UTF-8");
		headers.setContentType(type);
		headers.add("Accept", MediaType.APPLICATION_JSON.toString());
		HttpEntity<String> formEntity = new HttpEntity<String>(JSONObject.toJSONString(map), headers);
		String response = restTemplate.postForObject(url, formEntity,String.class);
		return JSONObject.parseObject(response);
	}
}
